package com.woniu.account.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.io.FileTypeUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.crypto.digest.MD5;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.woniu.account.dao.TCoachMapper;
import com.woniu.account.dao.TUserInfoMapper;
import com.woniu.account.dto.GetUserInfoByIdDTO;
import com.woniu.account.dto.MyFollowCoachDto;
import com.woniu.account.dto.UserInfoChangeDto;
import com.woniu.account.dto.UserLoginDTO;
import com.woniu.account.form.*;
import com.woniu.account.model.TCoach;
import com.woniu.account.model.TLoginLog;
import com.woniu.account.model.TUserInfo;
import com.woniu.account.service.TLoginLogService;
import com.woniu.account.service.TUserInfoService;
import com.woniu.account.client.GymServerClient;
import com.woniu.account.client.dto.GymBaseDto;
import com.woniu.core.exception.account.AccountException;
import com.woniu.core.exception.account.AccountExceptionCode;
import com.woniu.core.util.*;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.*;

import static com.woniu.core.exception.account.AccountExceptionCode.LOGIN_ERROR;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author 吴彦祖
 * @since 2024年04月02日
 */
@Service
public class TUserInfoServiceImpl extends ServiceImpl<TUserInfoMapper, TUserInfo> implements TUserInfoService {

    @Resource
    private TUserInfoMapper tUserInfoMapper;
    @Resource
    private TLoginLogService tLoginLogService;
    @Resource
    private RedisUtil redisUtil;
    @Resource
    private AliyunOSSUtil aliyunOSSUtil;
    @Resource
    private TCoachMapper tCoachMapper;
    @Resource
    private GymServerClient gymServerClient;
    @Override
    public GetUserInfoByIdDTO getUserInfoById(Integer id) throws Exception {
        TUserInfo tUserInfo = tUserInfoMapper.selectById(id);
        if(tUserInfo==null) throw new AccountException(AccountExceptionCode.NOT_FIND_THIS_USER);
        if(tUserInfo.getUserState()!=1) throw new AccountException("账号状态异常",400001);
        GetUserInfoByIdDTO dto = BeanUtil.toBean(tUserInfo, GetUserInfoByIdDTO.class);
        return dto;
    }

    @Override
    public TUserInfo userRegister(RegisterUserInfoForm form) throws Exception {
        List<TUserInfo> list = list(new QueryWrapper<TUserInfo>()
                .eq("user_name", form.getUserName()).or()
                .eq("user_phone", form.getUserPhone()));
        if (list.size()>0) throw new AccountException(AccountExceptionCode.THIS_ACCOUNT_HAS_BEEN_REGISTERED);
        TUserInfo tUserInfo = BeanUtil.toBean(form, TUserInfo.class);
        String salt = form.getUserName();
        int index = 0;
        int count = 2;
        MD5 md5 = new MD5(salt.getBytes(StandardCharsets.UTF_8),index,count);
        tUserInfo.setUserPass(md5.digestHex(form.getUserPass()));
        //注册时user状态为2：未认证
        tUserInfo.setUserState(2);
        save(tUserInfo);
        return tUserInfo;
    }

    @Override
    public UserLoginDTO userLogin(LoginUserInfoForm form) throws Exception {
        //判断账号密码是否正确
        TUserInfo admin = getOne(new QueryWrapper<TUserInfo>()
                .eq("user_name", form.getUserName()));
        if (admin==null) throw new AccountException("账号不存在",400001);
        String salt = form.getUserName();
        int index = 0;
        int count = 2;
        MD5 md5 = new MD5(salt.getBytes(StandardCharsets.UTF_8),index,count);
        if(!md5.digestHex(form.getUserPass()).equals(admin.getUserPass())){
            throw new AccountException("密码错误",400001);
        }
        if(admin.getUserState()== 0){
            throw new AccountException("账号状态异常",400001);
        }
        //获得返回对象
        UserLoginDTO dto = BeanUtil.toBean(admin, UserLoginDTO.class);
        //创建token
        Map<String,Object> maps = new HashMap<>();
        maps.put("userId", dto.getUserId());
        maps.put("userName", dto.getUserName());
        //创建短token
        String accessToken = JwtUtil.createToken(maps, 30,2);
        //创建长token,续期
        String refreshToken = JwtUtil.createToken(maps, 60 * 24,2);
        //token为键，长token为值，存入redis
        redisUtil.set(accessToken,refreshToken,24L*60L*60L);
        //将token放入响应头
        WebUtil.getResponse().setHeader("access",accessToken);
        WebUtil.getResponse().addHeader("Access-Control-Expose-Headers","access");

        //添加登录日志
        TLoginLog log = new TLoginLog();
        log.setLoginRole(1);
        log.setAccountId(admin.getUserId());
        log.setAccountName(admin.getUserName());
        String ipAddr = JwtUtil.getIpAddr(WebUtil.getRequest());
        log.setAddressIp(ipAddr);
        log.setLogInfo("登录成功");
        log.setLogDate(LocalDateTime.now());
        boolean b = tLoginLogService.save(log);
        if (!b) throw new AccountException(LOGIN_ERROR);
        return dto;
    }

    @Override
    public void sendLoginCode(String phone) throws Exception {
        //发送登录验证码时，要验证手机号是否注册过，注册过才发验证码
        QueryWrapper<TUserInfo> wrapper = new QueryWrapper<TUserInfo>();
        wrapper.eq("user_phone", phone);
        TUserInfo userInfo = tUserInfoMapper.selectOne(wrapper);
        if (userInfo == null) {
            throw new AccountException("手机号还未注册",400001);
        }
        String code = "123456";
        //redis保存一份验证码，为了登录时的验证
        redisUtil.set(RedisKeyUtil.sendLoginCodeKey(phone), code, 300);
        //一分钟内不能同时发送的标识
        redisUtil.set(RedisKeyUtil.sendLoginCodeInMinute(phone), "", 60);
        //判断24小时内的次数限定
        if (redisUtil.exist(RedisKeyUtil.sendCodeNumberOn24hour(phone))) {
            //存在就调用util自增1的方法，为了判断是否达到5条
            redisUtil.incr(RedisKeyUtil.sendCodeNumberOn24hour(phone));
        } else {
            //如果没有发送过的话，就要加1
            redisUtil.set(RedisKeyUtil.sendCodeNumberOn24hour(phone), "1", 24 * 60 * 60);
        }
    }

    @Override
    public UserLoginDTO coachCodeLogin(CoachCodeLoginForm form) throws Exception {
        //取出之前的登录验证码key
        //前端传过来的是用Interger的类型，这里就要强转
        int code = Integer.parseInt(redisUtil.get(RedisKeyUtil.sendLoginCodeKey(form.getCoachPhone())));
        if (form.getCode() != code) {
            if (!redisUtil.exist(RedisKeyUtil.errorTotal(form.getCoachPhone()))) {
                redisUtil.set(RedisKeyUtil.errorTotal(form.getCoachPhone()), "1", 5 * 60);
            } else {
                redisUtil.incr(RedisKeyUtil.errorTotal(form.getCoachPhone()));
            }
            throw new AccountException("验证码错误",400001);
        }
        //登录就不用判断了，因为收到了验证码，证明无异常,直接查
        QueryWrapper<TUserInfo> wrapper = new QueryWrapper<>();
        wrapper.eq("user_phone", form.getCoachPhone());
        TUserInfo tUserInfo = tUserInfoMapper.selectOne(wrapper);
        //获得返回对象
        UserLoginDTO dto = BeanUtil.toBean(tUserInfo, UserLoginDTO.class);
        //创建token
        Map<String,Object> maps = new HashMap<>();
        maps.put("userId", dto.getUserId());
        maps.put("userName", dto.getUserName());
        //创建短token
        String accessToken = JwtUtil.createToken(maps, 30,3);
        //创建长token,续期
        String refreshToken = JwtUtil.createToken(maps, 60 * 24,3);
        //token为键，长token为值，存入redis
        redisUtil.set(accessToken,refreshToken,24L*60L*60L);
        //将token放入响应头
        WebUtil.getResponse().setHeader("access",accessToken);
        WebUtil.getResponse().addHeader("Access-Control-Expose-Headers","access");

        //添加登录日志
        TLoginLog log = new TLoginLog();
        log.setLoginRole(1);
        log.setAccountId(tUserInfo.getUserId());
        log.setAccountName(tUserInfo.getUserName());
        String ipAddr = JwtUtil.getIpAddr(WebUtil.getRequest());
        log.setAddressIp(ipAddr);
        log.setLogInfo("登录成功");
        log.setLogDate(LocalDateTime.now());
        boolean b = tLoginLogService.save(log);
        if (!b) throw new AccountException(LOGIN_ERROR);
        return dto;
    }

    @Override
    public void userChangePassword(ChangePasswordForm form) throws Exception {
        System.out.println("33333333");
        Integer adminId = JwtUtil.getUserId(2);
        TUserInfo tUserInfo = tUserInfoMapper.selectById(adminId);
        if(tUserInfo==null){
            throw  new AccountException(AccountExceptionCode.NOT_FIND_THIS_USER);
        }
        String salt = tUserInfo.getUserName();
        int index=0;//盐的位移数，底层逻辑是通过位移进行加密的
        int count=2;//加密的次数
        //MD5的加密API
        MD5 md5 = new MD5(salt.getBytes(StandardCharsets.UTF_8), index, count);
        if(!tUserInfo.getUserPass().equals(md5.digestHex(form.getAccountPass()))){
            throw new AccountException("旧密码输入不正确",400001);
        }
        if(!form.getNewAccountPass().equals(form.getNewAccountPass1())){
            throw new AccountException("两次密码输入不同",400001);
        }
        UpdateWrapper<TUserInfo> wra = new UpdateWrapper<TUserInfo>();
        wra.set("user_pass",md5.digestHex(form.getNewAccountPass()));
        wra.eq("user_id",tUserInfo.getUserId());
        tUserInfoMapper.update(null,wra);
    }

    @Override
    public UserInfoChangeDto perfectInformation(updateUserInformationForm form) throws Exception {
        Integer userId = JwtUtil.getUserId(2);
        TUserInfo tUserInfo = tUserInfoMapper.selectById(userId);
        if(tUserInfo==null)throw new AccountException("用户不存在",40001);
        UserInfoChangeDto dto = BeanUtil.toBean(form, UserInfoChangeDto.class);
//        tUserInfo.setUserEmail(form.getCoachEmail());
//        tUserInfo.setUserPhone(form.getCoachPhone());
//        tUserInfo.setUserImage(form.getCoachImage());
//        tUserInfo.setUserNickname(form.getCoachNickname());
//        tUserInfo.setRemark(form.getRemark());
        tUserInfoMapper.updateById(tUserInfo);
        return dto;
    }

    @Override
    public String userRealNameAuthentication(RealNameAuthenticationForm form) throws Exception {
        String str = JwtUtil.realNameAuthentication(form.getCardId(), form.getRealName());
        if (!str.equals("true")) return "实名认证失败，请重试";
        Integer userId = JwtUtil.getUserId(2);
        if (userId==null) throw new AccountException(AccountExceptionCode.USER_NOT_LOGIN);
        TUserInfo tUserInfo = getOne(new QueryWrapper<TUserInfo>().eq("user_id", userId));
        if (tUserInfo!=null) {
            TUserInfo user = tUserInfoMapper.selectById(tUserInfo.getUserId());
            //认证后修改状态为1：正常
            user.setUserState(1);
            user.setUserRealname(form.getRealName());
            user.setUserIdnum(form.getCardId());
            user.setUserPhoto(form.getCardPhoto());
            Long age = JwtUtil.getAgeForIdcard(form.getCardId());
            String gender = JwtUtil.getSexFromIdCard(form.getCardId());
            user.setUserAge(age.intValue());
            if(gender.equals("女")){
                user.setUserGender(2);
            }else {
                user.setUserGender(1);
            }
            updateById(user);
        }else{
            return "请先注册";
        }
        return "实名认证通过";
    }

    @Override
    public PageInfo<MyFollowCoachDto> myFans(Integer pageNum, Integer pageSize, Integer userId) throws Exception {
        pageNum = (pageNum == null) ? 1 : pageNum;
        pageSize= (pageSize == null) ? 5 : pageSize;
        //先查询收藏列表总数
        long total = redisUtil.zcard(RedisKeyUtil.followList(userId));
        long pages = total%pageSize==0?total/pageSize:total/pageSize+1;
        long start = (pageNum-1)*pageSize;
        long end = pageSize*pageNum-1;
        if(redisUtil.zcard(RedisKeyUtil.followList(userId))!=0){
            Set<String> set = redisUtil.zrevrange(RedisKeyUtil.followList(userId), start, end);

            ArrayList<MyFollowCoachDto> data = new ArrayList<>();

            for(String coachId:set){
                TCoach tCoach = tCoachMapper.selectById(coachId);
                MyFollowCoachDto dto = new MyFollowCoachDto();
                dto.setCoachName(tCoach.getCoachName());
                dto.setCoachImage(tCoach.getCoachImage());
                GymBaseDto gymBaseDto = gymServerClient.findGymBaseByNum(tCoach.getGymNum()).getData();
                dto.setGymName(gymBaseDto.getGymName());
                dto.setGymNum(gymBaseDto.getGymNum());
                data.add(dto);
            }
            PageInfo<MyFollowCoachDto> pageInfo = new PageInfo<>();
            pageInfo.setTotal(total);
            pageInfo.setPages(pages);
            pageInfo.setCurrent(Long.valueOf(pageNum));
            pageInfo.setSize(Long.valueOf(pageSize));
            pageInfo.setList(data);
            return pageInfo;
        }else {
            //解决前端为null的时候控制台报错问题
            PageInfo<MyFollowCoachDto> pageInfo = new PageInfo<>();
            pageInfo.setCurrent(Long.valueOf(pageNum));
            pageInfo.setSize(Long.valueOf(pageSize));
            return pageInfo;

        }

    }

    @Override
    public String uploadImage(UpdateImageForm form) throws Exception {
        TUserInfo tUserInfo = tUserInfoMapper.selectById(form.getAccountId());
        String fileType = FileTypeUtil.getType(form.getImage().getInputStream());
        if(!fileType.equals("jpg")&&!fileType.equals("png")){
            throw new Exception("图片格式不正确");
        }

        String suffix = form.getImage().getOriginalFilename().substring(form.getImage().getOriginalFilename().lastIndexOf("."));
        String name = UUID.randomUUID().toString();
        //String path = aliyunOSSUtil.uploadFile(param.getImage().getInputStream(), name + suffix, "ryusai");
        //新的图片地址
        String url = aliyunOSSUtil.uploadFile(form.getImage().getInputStream(), name + suffix, "ryusai");
        //老的图片地址
        String oldUrl = tUserInfo.getUserImage();
        try {
            tUserInfoMapper.updateById(tUserInfo);
        }catch (Exception e){
            //没有新增成功
            aliyunOSSUtil.deleteFile("ryusai", FileUtil.getName(url));
        }
        //新增成功了删掉
        aliyunOSSUtil.deleteFile("ryusai",FileUtil.getName(oldUrl));
        tUserInfo.setUserImage(url);
        tUserInfoMapper.updateById(tUserInfo);
        return url;
    }

    @Override
    public void sendRegisterCode(String phone) throws Exception {
        //判断当前手机号在1分钟内是否发送过
        if(redisUtil.exist(RedisKeyUtil.sendRegisterCodeInMinute(phone))){
            throw new Exception("一分钟内不能再次发送验证码");
        }
        //根据手机号查询用户信息
        QueryWrapper<TUserInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_phone",phone);
        TUserInfo tUserInfo = tUserInfoMapper.selectOne(queryWrapper);
        if(tUserInfo!=null){
            throw new AccountException("手机号已被注册",400001);
        }

        //调用SMS工具发送短信验证码
//        String code = smsUtil.send(phone);
        String code = "147258";
        redisUtil.set(RedisKeyUtil.sendRegisterCodeKey(phone),code,300L);
        //保存一个1分钟过期的key
        redisUtil.set(RedisKeyUtil.sendRegisterCodeInMinute(phone),"",60L);
    }
}
